using System;
using System.Diagnostics.Contracts;
using System.Text;
using System.Net.Mail;

namespace FutureVision.Infrastructure
{
    public class MailService
    {
        public static MailService Default { get; private set; }

        static MailService()
        {
            Configure();
        }

        public static void Configure()
        {
            var configSection = EnterpriseServicesConfigurationSection.GetConfigurationSection();

            Default = new MailService(configSection.SmtpServer);
        }

        public MailService()
            : this(Default.smtpServer)
        {
        }

        public MailService(string smtpServer)
        {
            this.smtpServer = smtpServer;

            this.message = new MailMessage();
        }

        private string smtpServer;
        private MailMessage message;

        public MailService SmtpServer(string smtpServer)
        {
            this.smtpServer = smtpServer;

            return this;
        }

        public MailService Message(MailMessage message)
        {
            Contract.Requires(message != null);

            this.message = message;

            return this;
        }

        public MailService Message(MailMessageBuilder messageBuilder)
        {
            Contract.Requires(messageBuilder != null);

            this.message = messageBuilder.Build();

            return this;
        }

        public void Send()
        {
            var smtpClient = new SmtpClient(this.smtpServer);

            smtpClient.Send(this.message);
        }

        public bool TrySend(out Exception exception)
        {
            try
            {
                this.Send();

                exception = null;

                return true;
            }
            catch (Exception ex)
            {
                LogService.Default.Error("Failed to send email (from: {0}, to: {1}subject: {2}). Error: {3}", message.From, message.To, message.Subject, ex.ToString());

                exception = ex;

                return false;
            }
        }
    }

    public static class Message
    {
        public static MailMessageBuilder From(MailAddress address)
        {
            return new MailMessageBuilder().From(address);
        }

        public static MailMessageBuilder From(string address)
        {
            return new MailMessageBuilder().From(address);
        }
    }

    public class MailMessageBuilder
    {
        private readonly MailMessage message;

        public MailMessageBuilder()
        {
            this.message = new MailMessage();
        }

        public MailMessageBuilder From(MailAddress address)
        {
            Contract.Requires(address != null);

            this.message.From = address;

            return this;
        }

        public MailMessageBuilder From(string address)
        {
            return this.From(new MailAddress(address));
        }

        public MailMessageBuilder To(params MailAddress[] addresses)
        {
            Contract.Requires(addresses != null);

            foreach (var address in addresses)
            {
                this.message.To.Add(address);
            }

            return this;
        }

        public MailMessageBuilder To(string semiColonSeparatedAddresses)
        {
            return this.To(BuildMailAddressCollection(semiColonSeparatedAddresses));
        }

        public MailMessageBuilder Cc(params MailAddress[] addresses)
        {
            Contract.Requires(addresses != null);

            foreach (var address in addresses)
            {
                this.message.CC.Add(address);
            }

            return this;
        }

        public MailMessageBuilder Cc(string semiColonSeparatedAddresses)
        {
            return this.Cc(BuildMailAddressCollection(semiColonSeparatedAddresses));
        }

        public MailMessageBuilder Bcc(params MailAddress[] addresses)
        {
            Contract.Requires(addresses != null);

            foreach (var address in addresses)
            {
                this.message.Bcc.Add(address);
            }

            return this;
        }

        public MailMessageBuilder Bcc(string semiColonSeparatedAddresses)
        {
            return this.Bcc(BuildMailAddressCollection(semiColonSeparatedAddresses));
        }

        public MailMessageBuilder Subject(string subject, Encoding encoding)
        {
            this.message.Subject = subject;
            this.message.SubjectEncoding = encoding;

            return this;
        }

        public MailMessageBuilder Subject(string subject)
        {
            return this.Subject(subject, Encoding.UTF8);
        }

        public MailMessageBuilder Body(string body, Encoding encoding)
        {
            this.message.Body = body;
            this.message.BodyEncoding = encoding;

            return this;
        }

        public MailMessageBuilder HtmlBody(string body, Encoding encoding)
        {
            this.message.IsBodyHtml = true;
            this.message.Body = body;
            this.message.BodyEncoding = encoding;

            return this;
        }

        public MailMessageBuilder Body(string body)
        {
            return this.Body(body, Encoding.Default);
        }

        public MailMessageBuilder HtmlBody(string body)
        {
            return this.HtmlBody(body, Encoding.Default);
        }

        public MailMessageBuilder AlternateView(params AlternateView[] views)
        {
            foreach (var view in views)
            {
                this.message.AlternateViews.Add(view);
            }

            return this;
        }

        public MailMessage Build()
        {
            return this.message;
        }

        private static MailAddress[] BuildMailAddressCollection(string commaSeparatedAddresses)
        {
            Contract.Requires(commaSeparatedAddresses != null);

            var addresses = commaSeparatedAddresses.Split(',');

            var addressArray = new MailAddress[addresses.Length];

            for (int i = 0; i < addresses.Length; i++)
            {
                addressArray[i] = new MailAddress(addresses[i]);
            }

            return addressArray;
        }
    }
}